// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <memory>
#include <string>

#include <nebula/json/options.h>
#include <turbo/utility/status.h>
#include <nebula/util/key_value_metadata.h>
#include <turbo/base/macros.h>
#include <nebula/types/type_fwd.h>

namespace nebula::json {

    struct Kind {
        enum type : uint8_t {
            kNull,
            kBoolean,
            kNumber,
            kString,
            kArray,
            kObject,
            kNumberOrString
        };

        static const std::string &Name(Kind::type);

        static const std::shared_ptr<const KeyValueMetadata> &Tag(Kind::type);

        static Kind::type FromTag(const std::shared_ptr<const KeyValueMetadata> &tag);

        static turbo::Status ForType(const DataType &type, Kind::type *kind);
    };

    /// \class BlockParser
    /// \brief A reusable block-based parser for JSON data
    ///
    /// The parser takes a block of newline delimited JSON data and extracts Arrays
    /// of unconverted strings which can be fed to a Converter to obtain a usable Array.
    ///
    /// Note that in addition to parse errors (such as malformed JSON) some conversion
    /// errors are caught at parse time:
    /// - A null value in non-nullable column
    /// - Change in the JSON kind of a column. For example, if an explicit schema is provided
    ///   which stipulates that field "a" is integral, a row of {"a": "not a number"} will
    ///   result in an error. This also applies to fields outside an explicit schema.
    class TURBO_EXPORT BlockParser {
    public:
        virtual ~BlockParser() = default;

        /// \brief Reserve storage for scalars parsed from a block of json
        virtual turbo::Status reserve_scalar_storage(int64_t nbytes) = 0;

        /// \brief Parse a block of data
        virtual turbo::Status parse(const std::shared_ptr<Buffer> &json) = 0;

        /// \brief Extract parsed data
        virtual turbo::Status finish(std::shared_ptr<Array> *parsed) = 0;

        /// \brief Return the number of parsed rows
       [[nodiscard]] int32_t num_rows() const { return num_rows_; }

        /// \brief Construct a BlockParser
        ///
        /// \param[in] pool MemoryPool to use when constructing parsed array
        /// \param[in] options ParseOptions to use when parsing JSON
        /// \param[out] out constructed BlockParser
        static turbo::Status create(MemoryPool *pool, const ParseOptions &options,
                           std::unique_ptr<BlockParser> *out);

        static turbo::Status create(const ParseOptions &options, std::unique_ptr<BlockParser> *out);

    protected:
        TURBO_DISALLOW_COPY_AND_ASSIGN(BlockParser);

        explicit BlockParser(MemoryPool *pool) : pool_(pool) {}

        MemoryPool *pool_;
        int32_t num_rows_ = 0;
    };

}  // namespace nebula::json
